package com.rzgeo.rzmaplib.action;

import android.app.Activity;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;

import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.FeatureQueryResult;
import com.esri.arcgisruntime.data.FeatureTable;
import com.esri.arcgisruntime.data.Field;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.geometry.Geometry;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.GeometryType;
import com.esri.arcgisruntime.geometry.ImmutablePart;
import com.esri.arcgisruntime.geometry.Multipart;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.Polygon;
import com.esri.arcgisruntime.geometry.Polyline;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.mapping.view.Callout;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.mapping.view.SketchCreationMode;
import com.esri.arcgisruntime.mapping.view.SketchEditor;
import com.esri.arcgisruntime.mapping.view.SketchGeometryChangedEvent;
import com.esri.arcgisruntime.mapping.view.SketchGeometryChangedListener;
import com.rzgeo.rzmaplib.R;
import com.rzgeo.rzmaplib.util.RmlUtil;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * Created by zhanghang on 2018/1/27.
 */

public class RmlGeoEditAction implements ActionMode.Callback {
    enum Mode{
        split, none, merge
    }
    private Mode _editmode;
    private final MapView _mapView;
    private final SketchEditor _ske;
    private final GraphicsOverlay _tempGraphics;
    private final Activity _activity;
    private final FeatureLayer _targetLayer;
    private final SketchEditCallOut.OnResult _onSkHelper=new SketchEditCallOut.OnResult() {
        public String _errMsg;
        @Override
        public boolean OnFinish(final Geometry geometry) {

            if(geometry.getGeometryType()!= GeometryType.POLYLINE
                    &&geometry.getGeometryType()!= GeometryType.POLYGON){
                return false;
            }
            final ListenableFuture<FeatureQueryResult> selectFeature = _targetLayer.getSelectedFeaturesAsync();
            selectFeature.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        final FeatureTable ft = _targetLayer.getFeatureTable();

                        Iterator<Feature> iterator = selectFeature.get().iterator();
                        ArrayList<Feature> featuresSeled = new ArrayList<>();
                        int count = 0;
                        while (iterator.hasNext()) {
                            count++;
                            Feature feature = iterator.next();
                            if (ft.canDelete(feature))
                                featuresSeled.add(feature);
                        }
                        if (count != featuresSeled.size()) {
                            RmlUtil.Tip.Toast(String.format("有%个旧要素无法删除", count - featuresSeled.size()));
                            return;
                        }
                        if(!ft.canAdd()){
                            RmlUtil.Tip.Toast("图层无法添加新数据");
                            return;
                        }

                        if(_editmode==Mode.split){
                            DoSplit(geometry,featuresSeled,ft);
                        }else if(_editmode==Mode.merge){
                            DoMerge(geometry,featuresSeled,ft);
                        }
                    } catch (Exception e) {
                        RmlUtil.Log.Err(e);
                    }
                }
            });
            return true;
        }

        private void DoMerge(Geometry geometry, final ArrayList<Feature> featuresSeled, final FeatureTable ft) {
            final ArrayList<Geometry> geos = new ArrayList<>(featuresSeled.size()+1);
            geos.add(geometry);

            Field FID=RmlUtil.Features.getFIDField(ft.getFields());

            ArrayList<String>_featureTitles=new ArrayList<>();

            for(Feature f:featuresSeled){
                Geometry g = f.getGeometry();
                geos.add(g);

                String title = "";
                if(FID!=null){
                    title="ID："+f.getAttributes().get(FID.getName())+"\t";
                }
                if(g!=null){
                    if(g instanceof Polygon){
                        title+="面积:"+(int)GeometryEngine.area((Polygon)g);
                    }else if(g instanceof Polyline){
                        title+="长度:"+(int)GeometryEngine.length((Polyline)g);
                    }
                }
                _featureTitles.add(title);
            }

            RmlUtil.Dialog.List(
                    _activity,
                    "选择一个要素作为属性模板",
                    _featureTitles, null, 0,
                    new RmlUtil.Dialog.OnList() {
                @Override
                public void onConfirm(int select) {
                    Map<String, Object> attrs = featuresSeled.get(select).getAttributes();
                    Geometry geo = GeometryEngine.union(geos);
                    Feature nf = ft.createFeature(attrs, geo);
//                    final ListenableFuture<Void> ar = ft.addFeatureAsync(nf);

                    ArrayList<Feature> features2Add=new ArrayList<>(1);
                    features2Add.add(nf);

                    RmlUtil.Features.replaceFeature(ft, features2Add, featuresSeled, new RmlUtil.Features.OnResult() {
                        @Override
                        public void onResult() {
                            clearDraw();
                            _mode.finish();
                            RmlUtil.Tip.Toast("完成分割");
                        }

                        @Override
                        public void onFail() {
                            clearDraw();
                            _mode.finish();
                        }
                    });
                }

                @Override
                public void onCancel() {
                    RmlUtil.Tip.Toast("取消合并");
                }

                @Override
                public void onNeutral() {
                    RmlUtil.Tip.Toast("取消合并");
                }
            });
        }

        private void DoSplit(Geometry geometry, ArrayList<Feature> featuresSeled, final FeatureTable ft) {
            Polyline cutGeo= (Polyline) geometry;
            final ArrayList<Feature> features2Del2= new ArrayList<>();
            ArrayList<Feature> features2Add = new ArrayList<>();

            for(Feature f:featuresSeled){
                List<Geometry> newGeos = GeometryEngine.cut(f.getGeometry(), cutGeo);
                if(newGeos.size()>1){
                    //移除FID
                    for(Geometry newGeo:newGeos){
                        features2Add.add(
                                ft.createFeature(f.getAttributes(),newGeo));
                    }
                    features2Del2.add(f);
                }
            }
            RmlUtil.Features.replaceFeature(ft, features2Add, features2Del2, new RmlUtil.Features.OnResult() {
                @Override
                public void onResult() {
                    clearDraw();
                    _mode.finish();
                    RmlUtil.Tip.Toast("完成分割");
                }

                @Override
                public void onFail() {
                    clearDraw();
                    _mode.finish();
                }
            });
        }

        @Override
        public boolean PreFinish(final Geometry geo) {
            if(_errMsg==null)
                return true;
            else{
                RmlUtil.Tip.Toast(_errMsg);
                return false;
            }

        }

        @Override
        public void OnGeomtryChanged(final Geometry optionGeo) {
            QueryParameters qp=new QueryParameters();
            qp.setGeometry(optionGeo);
            qp.setReturnGeometry(true);

            final ListenableFuture<FeatureQueryResult> result = _targetLayer.selectFeaturesAsync(qp, FeatureLayer.SelectionMode.NEW);
            result.addDoneListener(new Runnable() {

                @Override
                public void run() {
                    try {

                        Iterator<Feature> iterator = result.get().iterator();
                        ArrayList<Geometry> geos = new ArrayList<>();
                        while (iterator.hasNext()) {
                            Feature feature = iterator.next();
                            geos.add(feature.getGeometry());
                        }

                        boolean valid=true;
                        if(_editmode==Mode.split){
                            if(geos.size()<1){
                                _errMsg="分割操作必须先选中一个图层中的要素";
                            }else if(optionGeo.getGeometryType()!=GeometryType.POLYLINE){
                                _errMsg="分割要素必须是线";
                            }else{
                                Polyline cutGeo = (Polyline) optionGeo;

                                valid=false;
                                for(Geometry toCut:geos){
                                    if(GeometryEngine.cut(toCut,cutGeo).size()>1){
                                        valid=true;
                                    }
                                }

                                _errMsg=valid?null:"未发现有效分割";
                            }
                        }else if(_editmode==Mode.merge){
//                            geos.add();

                            if(geos.size()<2){
                                valid=false;
                            }else{
                                for(int i=0;i<geos.size();i++){
                                    if(!GeometryEngine.intersects(optionGeo,geos.get(i))){
                                        valid=false;
                                        break;
                                    }
                                }
                            }
                            _errMsg=valid?null:"融合多边形必须和图层中的两个图斑相交才能融合";
                        }
                    }catch (Exception e){
                        RmlUtil.Log.Err(e);
                    }
                }
            });
        }

        @Override
        public void OnCancel() {
            clearDraw();

        }
    };
    private SketchEditor _oldSKE;
    private Geometry _curentGeo;
    private ActionMode _mode;
    private SketchEditCallOut _SketchEdit_callOut;


    public RmlGeoEditAction(Activity activity, MapView mapView, FeatureLayer targetLayer) {
        _activity=activity;
        _mapView = mapView;
        _targetLayer=targetLayer;
        _ske = new SketchEditor();
        _tempGraphics=new GraphicsOverlay();
        _targetLayer.setSelectionWidth(2);
        _SketchEdit_callOut =new SketchEditCallOut(activity,mapView,_onSkHelper);

        _ske.addGeometryChangedListener(_SketchEdit_callOut);
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        _mode=mode;
        MenuInflater inflater = _activity.getMenuInflater();
        inflater.inflate(R.menu.rml_geo_edit, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        _oldSKE = _mapView.getSketchEditor();
        _mapView.setSketchEditor(_ske);
        _mapView.getGraphicsOverlays().add(_tempGraphics);
        return false;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        if (item.getItemId() == R.id.act_undo) {
            clearDraw();
        }
        else if (item.getItemId() == R.id.act_split) {
            GeometryType gt = _targetLayer.getFeatureTable().getGeometryType();
            if(gt!=GeometryType.POLYLINE&&gt!=GeometryType.POLYGON){
                RmlUtil.Tip.Toast("只有面和线能够进行分割操作");
                return true;
            }
            clearDraw();
            RmlUtil.Tip.Toast("点击地图画绘制分割线，分割线需要完全切过被分割物体");
            _ske.start(SketchCreationMode.POLYLINE);
            _editmode=Mode.split;
        }else if(item.getItemId() == R.id.act_merge){
            GeometryType gt = _targetLayer.getFeatureTable().getGeometryType();
            if(gt!=GeometryType.POLYGON){
                RmlUtil.Tip.Toast("只有面能够进行融合操作");
                return true;
            }
            clearDraw();
            RmlUtil.Tip.Toast("点击地图画绘融合多边形，与多边形相交的");
            _ske.start(SketchCreationMode.POLYGON);
            _editmode=Mode.merge;
        }
        else if (item.getItemId() == R.id.act_cancel) {
            clearDraw();
            _mode.finish();
        }
        else if (item.getItemId() == R.id.act_ok) {
            if(_curentGeo==null&&_tempGraphics.getGraphics().size()==0)
            {
                RmlUtil.Tip.Toast("没有数据需要保存");
                _mode.finish();
                return true;
            }
            if(_targetLayer==null)
            {
                RmlUtil.Tip.Toast("没有图层");
                _mode.finish();
                return true;
            }
            if(!_targetLayer.getFeatureTable().isEditable())
            {
                RmlUtil.Tip.Toast("图层不可编辑");
                _mode.finish();
                return true;
            }

//            ArrayList<Geometry>geos=new ArrayList<>(
//                    1+_tempGraphics.getGraphics().size());
//            geos.add(_curentGeo);
//            for(Graphic g:_tempGraphics.getGraphics()){
//                geos.add(g.getGeometry());
//            }
//
//            final FeatureTable ft = _targetLayer.getFeatureTable();
//            ArrayList<Feature>features=new ArrayList<>(geos.size());
//
//            for(Geometry g:geos){
//                Feature f = ft.createFeature();
//                f.setGeometry(g);
//                features.add(f);
//            }
//            ft.addFeaturesAsync(features);
//            ft.addDoneLoadingListener(new Runnable() {
//                @Override
//                public void run() {
//                    RmlUtil.Tip.Toast("添加完成");
//                    ft.removeDoneLoadingListener(this);
//                }
//            });
            _mode.finish();
        }
        else {
            return false;
        }
        return true;
    }

    private void clearDraw() {
            _curentGeo = null;
            _editmode=Mode.none;
            if (_ske.getGeometry() != null)
                _ske.clearGeometry();
            else if (_tempGraphics.getGraphics().size() > 0)
                _tempGraphics.getGraphics().remove(_tempGraphics.getGraphics().size() - 1);

            _targetLayer.clearSelection();
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        _mapView.setSketchEditor(_oldSKE);
        _mapView.getGraphicsOverlays().remove(_tempGraphics);
        _tempGraphics.getGraphics().clear();
    }


    void pushLast() {
        if (_curentGeo == null) {
            return;
        } else if (_curentGeo instanceof Point) {
            _tempGraphics.getGraphics().add(
                    new Graphic(_curentGeo, RmlUtil.Copy.copy(_ske.getSketchStyle().getVertexSymbol()))
            );
        } else if (_curentGeo instanceof Polyline) {
            _tempGraphics.getGraphics().add(
                    new Graphic(_curentGeo, RmlUtil.Copy.copy(_ske.getSketchStyle().getLineSymbol()))
            );

        } else if (_curentGeo instanceof Polygon) {

            _tempGraphics.getGraphics().add(
                    new Graphic(_curentGeo, RmlUtil.Copy.copy(_ske.getSketchStyle().getFillSymbol()))
            );
        } else {
            RmlUtil.Log.Err("不支持几何类型" + _curentGeo.getGeometryType().toString());
        }
        _curentGeo = null;
    }

    private static class SketchEditCallOut implements View.OnClickListener,SketchGeometryChangedListener{

        private final OnResult _onResult;

        public interface OnResult{
            boolean OnFinish(Geometry geometry);

            /***
             * 点击完成键时调用，如果返回false则无法完结
             * @param geometry
             * @return
             */
            boolean PreFinish(Geometry geometry);
            void OnGeomtryChanged(Geometry geo);
            void OnCancel();
        }

        private final View _content;
        private View _oldContent;
        private final Activity _activity;
        private final MapView _mapView;

        public SketchEditCallOut(Activity activity, MapView mapView, OnResult onResult) {
            _activity=activity;
            _mapView=mapView;
            _onResult=onResult;

            LayoutInflater inflater = _activity.getLayoutInflater();
            _content=inflater.inflate(R.layout.comp_mapedit_callout,null);

            _content.findViewById(R.id.act_cancel).setOnClickListener(this);
            _content.findViewById(R.id.act_ok).setOnClickListener(this);
            _content.findViewById(R.id.act_undo).setOnClickListener(this);
            _content.findViewById(R.id.act_redo).setOnClickListener(this);
        }
//
//        public void load(){
////            _mapView.getSketchEditor().addGeometryChangedListener(this);
//        }
//
//        public void unload(){
////            _mapView.getSketchEditor().removeGeometryChangedListener(this);
//
//            Callout co = _mapView.getCallout();
//            co.setContent(_oldContent);
//            co.dismiss();
//        }

        @Override
        public void onClick(View v) {

            if (v.getId()==R.id.act_undo){
                _mapView.getSketchEditor().undo();
            }else if (v.getId()==R.id.act_redo){
                _mapView.getSketchEditor().redo();
            } else if (v.getId()==R.id.act_cancel){
                _onResult.OnCancel();
                unload();
            }else if (v.getId()==R.id.act_ok){
                if(_onResult.PreFinish(_mapView.getSketchEditor().getGeometry())){
                    unload();
                    _onResult.OnFinish(_mapView.getSketchEditor().getGeometry());
                }
            }
        }

        private void unload() {
            if(_mapView.getCallout().getContent()==_content&&_oldContent!=null){
                _mapView.getCallout().setContent(_oldContent);
            }
            _mapView.getCallout().dismiss();
        }


        @Override
        public void geometryChanged(SketchGeometryChangedEvent e) {
            SketchEditor ske=_mapView.getSketchEditor();
            if (!ske.isSketchValid())
                return;


            if(ske.getSketchCreationMode()==SketchCreationMode.POLYGON){

                Polygon po = (Polygon) ske.getGeometry();
                if(po.getParts().size()==0||po.getParts().get(0).getPointCount()<3){
                    return;
                }
            }else if(ske.getSketchCreationMode()==SketchCreationMode.POLYLINE){

                Polyline pl = (Polyline) ske.getGeometry();
                if(pl.getParts().size()==0||pl.getParts().get(0).getPointCount()<2){
                    return;
                }
            }else{
                return;
            }


            Geometry geo = _mapView.getSketchEditor().getGeometry();
            Point cp;
            if(geo instanceof Multipart){
                Multipart po= (Multipart) geo;
                ImmutablePart p1 = po.getParts().get(po.getParts().size() - 1);
                cp=p1.getEndPoint();
            }else{
                return;
            }

            if(cp==null)
                return;

            if(_mapView.getCallout().getContent()!=_content){
                _oldContent=_mapView.getCallout().getContent();
                _mapView.getCallout().setContent(_content);
            }

            _mapView.getCallout().setLocation(cp);
            _mapView.getCallout().setContent(_content);
            _mapView.getCallout().show();
            _onResult.OnGeomtryChanged(geo);
        }
    }
}
